package com.info.zhiduoduo.wxsdk;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.info.zhiduoduo.wxsdk.api.WxApi;
import com.info.zhiduoduo.wxsdk.config.IWxAccessTokenCallbackConfig;
import com.info.zhiduoduo.wxsdk.config.WxZddMakerConfig;
import com.info.zhiduoduo.wxsdk.domain.app.WxAuthorizeAccessToken;
import com.info.zhiduoduo.wxsdk.domain.app.WxAuthorizeUserInfo;
import com.info.zhiduoduo.wxsdk.domain.app.WxNormalAccessToken;
import com.info.zhiduoduo.wxsdk.domain.app.WxShareSignature;
import com.info.zhiduoduo.wxsdk.domain.app.WxTempleteMessage;
import com.info.zhiduoduo.wxsdk.domain.app.WxUserBaseInfo;
import com.info.zhiduoduo.wxsdk.utils.LogUtil;
import com.info.zhiduoduo.wxsdk.utils.StringUtil;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Formatter;
import java.util.UUID;

/**
 * Created by Administrator on 2017/10/8 0008.
 */
@Service
public class WxApiService {

	@Autowired
	private WxZddMakerConfig wxZddMakerConfig;

	/** 与实际业务模块交互的回调 */
	@Autowired
	private IWxAccessTokenCallbackConfig accessTokenCallback;

	//************************************** 公众号/网页应用 ************************************//

	/**
	 * 网页授权方式获取微信用户信息
	 *
	 * @param accesstoken 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
	 * @param openid
	 * @return
	 */
	public WxAuthorizeUserInfo getAuthorizeUserInfo(String accesstoken, String openid) {
		String res = WxApi.getAuthorizeUserInfo(accesstoken, openid);
		return JSON.parseObject(res, WxAuthorizeUserInfo.class);
	}

	/**
	 * 普通方式获取微信用户信息
	 *
	 * @param openid
	 * @return
	 */
	public WxUserBaseInfo getWxUserInfo(String openid) {
		return getWxUserInfo(openid, wxZddMakerConfig.getAppid(), wxZddMakerConfig.getSecret());
	}

	public WxUserBaseInfo getWxUserInfo(String openid, String appid, String secret) {
		WxNormalAccessToken accessToken = getNormalAccessToken(appid, secret);
		if (accessToken == null) {
			return null;
		}
		String res = WxApi.getNormalUserInfo(accessToken.getAccess_token(), openid);
		return JSON.parseObject(res, WxUserBaseInfo.class);
	}

	/**
	 * 网站应用/公众号 获得网页授权信息，转化成map 第二步：通过code换取网页授权access_token
	 * 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
	 * 微信平台返回数据:
	 *
	 * @param code
	 * @return
	 */
	public WxAuthorizeAccessToken getAuthorizeAccessToken(String code) {
		String appid = wxZddMakerConfig.getAppid();
		String secret = wxZddMakerConfig.getSecret();
		return getAuthorizeAccessToken(code, appid, secret);
	}

	/**
	 * 网站应用/公众号 获得网页授权信息，转化成map 第二步：通过code换取网页授权access_token
	 * 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
	 * 微信平台返回数据:
	 *
	 * @param code
	 * @return
	 */
	public WxAuthorizeAccessToken getAuthorizeAccessToken(String code, String appid, String secret) {
		String res = WxApi.getAuthorizeAccessToken(appid, secret, code);
		return JSON.parseObject(res, WxAuthorizeAccessToken.class);
	}

	/**
	 * 公众号应用 获取最新的AccessToken
	 * 获取当前服务器普通AccessToken(此accesstoken与网页授权token不同)
	 *
	 * @return
	 */
	public WxNormalAccessToken getNormalAccessToken() {
		return getNormalAccessToken(wxZddMakerConfig.getAppid(), wxZddMakerConfig.getSecret());
	}

	public WxNormalAccessToken getNormalAccessToken(String appid, String secret) {
		long curTime = System.currentTimeMillis();
		WxNormalAccessToken latestWxNormalAccessToken = accessTokenCallback.getLatestAccessToken(appid);
		LogUtil.info("WxApiService.getNormalAccessToken latestWxNormalAccessToken = " + latestWxNormalAccessToken);
		LogUtil.info("wxappid = " + appid);
		if (latestWxNormalAccessToken == null || StringUtil.isEmpty(latestWxNormalAccessToken.getAccess_token())) {
			LogUtil.info("getNormalAccessToken : 为空,获取新的token");
			latestWxNormalAccessToken = applyNormalAccessTokenAndJsTicket(appid, secret);
		} else if ((curTime - latestWxNormalAccessToken.getCreatetime().getTime()) / 1000 > 5400) {
			LogUtil.info("getNormalAccessToken : 超时,重新申请token");
			latestWxNormalAccessToken = applyNormalAccessTokenAndJsTicket(appid, secret);
		}
		return latestWxNormalAccessToken;
	}

	/**
	 * 公众号应用 请求新的AccessToken
	 * 获取当前服务器普通AccessToken(此accesstoken与网页授权token不同)
	 * <p>
	 * 每一小时通过定时任务刷新
	 * <p>
	 * 获取新的token后通过回调给具体业务,保存最新的token
	 * <p>
	 * 微信平台返回数据:
	 * {"access_token":"ACCESS_TOKEN","expires_in":7200}
	 *
	 * @return
	 */
	public WxNormalAccessToken applyNormalAccessTokenAndJsTicket() {
		return applyNormalAccessTokenAndJsTicket(wxZddMakerConfig.getAppid(), wxZddMakerConfig.getSecret());
	}

	public WxNormalAccessToken applyNormalAccessTokenAndJsTicket(String appid, String secret) {
		try {
			LogUtil.info("applyNormalAccessTokenAndJsTicket");
			String resp = WxApi.getNormalAccessToken(appid, secret);
			if (StringUtil.isEmpty(resp)) {
				return null;
			}
			WxNormalAccessToken wxNormalAccessToken = JSON.parseObject(resp, WxNormalAccessToken.class);
			String jsTicket = WxApi.applyJsTicket(wxNormalAccessToken.getAccess_token());
			wxNormalAccessToken.setJsticket(jsTicket);
			wxNormalAccessToken.setCreatetime(new Date());
			accessTokenCallback.saveNewNormalAccessToken(wxNormalAccessToken, appid);
			return wxNormalAccessToken;
		} catch (Exception e) {
			e.printStackTrace();
			LogUtil.info("applyNormalAccessTokenAndJsTicket 报错 : " + e.getMessage());
		}
		return null;
	}

	/**
	 * 公众号 分享url 获取签名
	 *
	 * @param shareUrl
	 * @return
	 */
	public WxShareSignature getShareSignature(String shareUrl) {
		return getShareSignature(shareUrl, wxZddMakerConfig.getAppid(), wxZddMakerConfig.getSecret());
	}

	/**
	 * 公众号 分享url 获取签名
	 *
	 * @param shareUrl
	 * @return
	 */
	public WxShareSignature getShareSignature(String shareUrl, String appid, String secret) {
		WxNormalAccessToken normalAccessToken = getNormalAccessToken(appid, secret);
		if (normalAccessToken == null) {
			return null;
		}
		String jsticket = normalAccessToken.getJsticket();
		String nonce_str = create_nonce_str();
		String timestamp = create_timestamp();
		String signature = "";

		//注意这里参数名必须全部小写，且必须有序
		String string1 = "jsapi_ticket=" + jsticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + shareUrl;

		try {
			MessageDigest crypt = MessageDigest.getInstance("SHA-1");
			crypt.reset();
			crypt.update(string1.getBytes(StandardCharsets.UTF_8));
			signature = byteToHex(crypt.digest());
		} catch (NoSuchAlgorithmException e) {
			LogUtil.info(e.getMessage());
		}

		WxShareSignature wxShareSignature = new WxShareSignature();
		wxShareSignature.setAppId(appid);
		wxShareSignature.setNonceStr(nonce_str);
		wxShareSignature.setTimestamp(timestamp);
		wxShareSignature.setSignature(signature);
		return wxShareSignature;
	}

	/**
	 * 公众号 发送封装的模板消息
	 *
	 * @param message 模板消息对象
	 * @return 是否发送成功
	 */
	public boolean sendTempleteMessage(WxTempleteMessage message) {
		return sendTempleteMessage(message, wxZddMakerConfig.getAppid(), wxZddMakerConfig.getSecret());
	}

	public boolean sendTempleteMessage(WxTempleteMessage message, String appid, String secret) {
		WxNormalAccessToken accessToken = getNormalAccessToken(appid, secret);
		if (accessToken == null) {
			return false;
		}
		String messageStr = JSON.toJSONString(message);
		String resp = WxApi.sendTempleteMessage(accessToken.getAccess_token(), messageStr);
		if (StringUtil.isEmpty(resp)) {
			return false;
		}
		int errcode = (int) JSONObject.parseObject(resp).get("errcode");
		return errcode == 0;
	}

	public static String create_nonce_str() {
		return UUID.randomUUID().toString();
	}

	public static String create_timestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}

	private static String byteToHex(final byte[] hash) {
		Formatter formatter = new Formatter();
		for (byte b : hash) {
			formatter.format("%02x", b);
		}
		String result = formatter.toString();
		formatter.close();
		return result;
	}

}
